2

I have three models :

class LevelTwoArea < ActiveRecord::Base
  has_many :places, dependent: :restrict
end

class Place < ActiveRecord::Base
  belongs_to :level_two_area
  has_many   :producers, dependent: :restrict

  validates_presence_of :level_two_area_id
end

class Producer < ActiveRecord::Base
  belongs_to :place

  validates_presence_of :place_id
end

I also have a controller with an ActiveScaffold per model.

Problem is, when i want to create a new Place, the scaffold spits an error to the log :

ActiveScaffold::ReverseAssociationRequired (Association producers: In order 
to support :has_one and :has_many where the parent record is new and the child 
record(s) validate the presence of the parent, ActiveScaffold requires the 
reverse association (the belongs_to).)

I don't understand why...

Notice that :

  • Other similar scaffolds do work
  • i can create records and use associations in the console :

    >> Place.new name: 'PONVOGO', level_two_area_id: 10144
    => #<Place id: nil, name: "PONVOGO", latitude: nil, longitude: nil, producers_count: nil, level_two_area_id: 10144, created_at: nil, updated_at: nil, marker: nil>
    >> _.save
    => true
    >> Producer.first.place.producers
    => [#<Producer id: 1, name: "KOFFI YAO GREGOIRE", place_id: 43, created_at: nil, updated_at: nil>]
    
  • The problem disappears when i remove the has_many :producers, though the association macros look perfectly normal

  • The problem won't disappear if i remove the dependent: :restrict option
  • i have a producers_census column on my Place model, i suspect this to mess things up but would like to have confirmation before doing heavy refactoring. Removing this column from the scaffold won't fix the problem.

fields on the places table :

Column             |            Type             |                                  
--------------------------------------------------
 id                | integer                     | non NULL (PK)       
 name              | character varying(50)       | non NULL
 latitude          | double precision            | 
 longitude         | double precision            | 
 producers_census  | integer                     | 
 level_two_area_id | integer                     | non NULL (FK)
 created_at        | timestamp without time zone | 
 updated_at        | timestamp without time zone | 
 marker            | geometry                    | 

my entire PlacesController :

class PlacesController < ApplicationController
  active_scaffold :place do |conf|
    conf.columns =  :name,
                    :level_two_area,
                    :latitude,
                    :longitude,
                    :producers_census
    export.columns.add  :id, 
                        :level_two_area_id

    [:latitude, :longitude].each {|column| conf.columns[column].options[:format] = "%.14f" }

    [               
      create,
      update,
      delete,
      batch_update 
    ].each do |action|
       action.link.security_method = :can_see_link?
    end
    batch_destroy.link.each {|sublink| sublink.security_method = :can_see_link? }

  end
end 

i'm on rails (3.0.5) / active_scaffold_vho (3.0.17) / ruby (1.9.2p180)

m_x
  • 12,357
  • 7
  • 46
  • 60
  • Please show the console command that you use (that avoids the problem) and please show the controller code that experiences the problem. It will help in diagnosis if you show the code that has the error which is the controller. It will also help in diagnosis if you could do a sql describe table to show what got built. – Michael Durrant Feb 01 '12 at 14:04
  • added precisions you asked for. – m_x Feb 01 '12 at 14:17

1 Answers1

3

While I've never seen this ActiveScaffold exception before, I think this is what's going on:

The reverse association is set up with the :inverse_of option, and its effects will be visible only while the records are unsaved. This is why you don't see it in your console experiment.

Try this:

place = Place.new
=> #<Place ...>
producer = place.producers.build
=> #<Producer ...>

producer.place
=> nil
place.producers.first.place
=> nil

These records will fail your validation, when built entirely in memory. If you never build a place and a producer at the same time, then you don't have to worry about this. If you do, however, then your validation rule also should be written to check for the existence of :place (i.e. the object), not :place_id:

validates_presence_of :place

I don't know which build/creation mechanism ActiveScaffold uses, but I would guess that if they throw this exception, that they may build things in memory.

So, to fix this, try:

class Producer < ActiveRecord::Base
  belongs_to :place, inverse_of: producers

  validates_presence_of :place
end

class Place < ActiveRecord::Base
  has_many :producers, inverse_of: place
end

Note: ActiveRecord used to not support :inverse_of on the has_many side and that limitation used to be commented in the source. As of Rails 3.1, this seems fixed. If you amend your classes with :inverse_of as shown, then the console experiment with in-memory objects should return the associated object.

You may want to review the Rails API docs on bi-directional associations

Wolfram Arnold
  • 7,159
  • 5
  • 44
  • 64
  • Thanks for the answer, i was desperate (+1 for your time). What you say makes sense, but it won't work. I tried your fix and the same error pops up. Weird thing is that i never create a producer through a place ! Even weirder : other similar AS controllers do work with similar associations. Even more weird : it used to work... and i never saw it was broken until now (never had the time to write the tests, and now i wish i could jump back in time and punch my old self in the nuts in punishment for such frivolity) – m_x Feb 09 '12 at 09:15
  • @m_x Have you tried tracing the code in the debugger, and/or inspecting the ActiveScaffold code to see where it throws this exception? When I don't know any more, I tend to read through the sources and/or set breakpoints. An editor like RubyMine help a lot in that because you can easily browse and search gems and the debugger integration is also fantastic. Sorry I couldn't help. – Wolfram Arnold Feb 09 '12 at 18:24
  • ...that's what i wanted to avoid... i already spent a ludicrous time debugging another problem in AS. I think that i will kick AS out from any other new project i do, i'm tired of its weird behaviour. As to RubyMine, i already considered purchasing an individual license a while ago, but i'm a bit short on money. Anyway, thanks for your time. – m_x Feb 10 '12 at 08:31
  • wow thanks ! your answer definitely put me on the right tracks. As i suspected the name of the `producers_census` column was the culprit. I'll edit your answer and accept it, because you definitely deserve the bounty. – m_x Feb 10 '12 at 11:29
  • @m_x: We've rejected your [suggested edit](http://stackoverflow.com/suggested-edits/198918) because it's worded as a reply to the post; if you want to make a correction, it should read naturally. Wolfram: [m_x's suggested edit](http://stackoverflow.com/suggested-edits/198918) may have interesting information that you'd like to edit in. – Gilles 'SO- stop being evil' Feb 10 '12 at 12:39
  • @m_x I saw your edits, I can't quite say that I understand it either. Perhaps you want to stick them in a comment, so that the record of your epiphany is there? Thanks for accepting my answer. – Wolfram Arnold Feb 10 '12 at 20:56
  • ok, so just for the record, here it is : i just added `conf.columns[:producers].association.reverse = :place` in my controller. As i said, no idea why the reverse association is needed. – m_x Feb 13 '12 at 08:46