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 :
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
dependent: :restrict
optionproducers_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)
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